//
//  EncryptorXXTEA.m
//  Etrans
//
//  Created by fhd on 12-5-10.
//  Copyright 2012 __MyCompanyName__. All rights reserved.
//

#import "EncryptorXXTEA.h"
#include <stdlib.h>
#include <memory.h>

@implementation EncryptorXXTEA

int toIntArray(int ** ppresult, const unsigned char * pData, unsigned nDataLen, int includeLength)
{
	if (ppresult && pData && nDataLen)
	{
		int n = (((nDataLen & 3) == 0) ? ((unsigned int)nDataLen >> 2)
				 : (((unsigned int)nDataLen >> 2) + 1));
		int i = 0;
		
		if (includeLength) {
			*ppresult = (int*)malloc(sizeof(int) * (n + 1));
			memset(*ppresult, 0, sizeof(int) * (n + 1));
			(*ppresult)[n] = nDataLen;
			n += 1;
		} else {
			*ppresult = (int*)malloc(sizeof(int) * n);
			memset(*ppresult, 0, sizeof(int) * n);
		}
		for (i = 0; i < nDataLen; i++) {
			(*ppresult)[(unsigned int)i >> 2] |= (0x000000ff & pData[i]) << ((i & 3) << 3);
		}
		return n;
	}
	return 0;
}

int toByteArray(unsigned char ** ppresult, int * pData, int nDataLen, int includeLength)
{
	if (ppresult && pData && nDataLen)
	{
		int n = nDataLen << 2;
		int i = 0;
		
		if (includeLength) {
			int m = pData[nDataLen - 1];
			
			if (m > n) {
				return 0;
			} else {
				n = m;
			}
		}
		*ppresult = (unsigned char*)malloc(sizeof(unsigned char) * n);
		
		for (i = 0; i < n; i++) {
			(*ppresult)[i] = (unsigned char) ((pData[i >> 2] >> ((i & 3) << 3)) & 0xff);
		}
		return n;
	}
	return 0;
}

//#define MX ((unsigned int)z >> 5 ^ y << 2) + ((unsigned int)y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z)
#define MX (((unsigned int)z >> 5 ^ y << 2) + (((unsigned int)y >> 3) ^ (z << 4))) ^ ((sum ^ y)  + (k[(p & 3) ^ e] ^ z))

int * raw_encrypt(int * v, int vlen, int * k, int klen)
{
	int n = (int)vlen - 1;
	if (n < 1)
	{
		return v;
	}
	else
	{
		int * key = NULL;
		if (klen < 4)
		{
			key = (int*)malloc(sizeof(int) * 4);
			memset(key, 0, sizeof(int) * 4);
			memcpy(key, k, sizeof(int) * klen);
			k = key;
		}
		
		{
			int z = v[n], y = v[0], delta = 0x9E3779B9, sum = 0, e;
			int p, q = 6 + 52 / (n + 1);
			while (q-- > 0) {
				sum += delta;
				e = (unsigned int)sum >> 2 & 3;
				for (p = 0; p < n; p++) {
					y = v[p + 1];
					z = (v[p] += MX);
				}
				y = v[0];
				z = (v[n] += MX);
			}
		}
		if (key)
			free(key);
	}
	
	return v;
}

int * raw_decrypt(int * v, int vlen, int * k, int klen)
{
	int n = (int)vlen - 1;
	if (n < 1)
	{
		return v;
	}
	else
	{
		int * key = NULL;
		if (klen < 4) {
			key = (int*)malloc(sizeof(int) * 4);
			memset(key, 0, sizeof(int) * 4);
			memcpy(key, k, sizeof(int) * klen);
			k = key;
		}
		{
			//delta = 0x9E3779B9
			int z = v[n], y = v[0], delta = 0x9E3779B9, sum, e;
			int p, q = 6 + 52 / (n + 1);
			sum = q * delta;
			while (sum != 0) {
				e = (unsigned int)sum >> 2 & 3;
				for (p = n; p > 0; p--) {
					z = v[p - 1];
					y = (v[p] -= MX);
				}
				z = v[n];
				y = (v[0] -= MX);
				sum -= delta;
			}
		}
		
		if (key)
			free(key);
	}
	
	return v;
}

int xxtea_encrypt(unsigned char ** ppresult, const unsigned char* pData, int nDataLen, const unsigned char* pKey, int nKeyLen)
{
	if (!pData || nKeyLen == 0)
	{
		return 0;
	}
	else
	{
		int * data = NULL;
		int * key = NULL;
		
		int datalen = toIntArray(&data,pData,nDataLen,1);
		int keylen = toIntArray(&key,pKey,nKeyLen,0);
		int retlen = 0;
		
		if (data && key)
		{
			retlen = toByteArray(ppresult, raw_encrypt(data, datalen, key, keylen), datalen, 0);
		}
		if (data)
			free(data);
		if (key)
			free(key);
		return retlen;
	}
	return 0;
}

int xxtea_decrypt(unsigned char ** ppresult, const unsigned char* pData, int nDataLen, const unsigned char* pKey, int nKeyLen)
{
	if (!pData || nKeyLen == 0)
	{
		return 0;
	}
	else
	{
		int * data = NULL;
		int * key = NULL;
		
		int datalen = toIntArray(&data,pData,nDataLen,0);
		int keylen = toIntArray(&key,pKey,nKeyLen,0);
		int retlen = 0;
		
		if (data && key)
		{
			retlen = toByteArray(ppresult, raw_decrypt(data, datalen, key, keylen), datalen, 1);
		}
		if (data)
			free(data);
		if (key)
			free(key);
		return retlen;
	}
	
	return 0;
}

+ (NSString*)hexStringForData:(NSData*)data
{
    if (data == nil) {
        return nil;
    }
	
    NSMutableString* hexString = [NSMutableString string];
	
    const unsigned char *p = [data bytes];
    
    for (int i=0; i < [data length]; i++) {
        [hexString appendFormat:@"%02X", *p++];
    }
    return hexString;
}

+ (NSData*)dataForHexString:(NSString*)hexString
{
    if (hexString == nil) {
        return nil;
    }
    
    const char* ch = [[hexString lowercaseString] cStringUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* data = [NSMutableData data];
    while (*ch) {
        char byte = 0;
        if ('0' <= *ch && *ch <= '9') {
            byte = *ch - '0';
        } else if ('a' <= *ch && *ch <= 'f') {
            byte = *ch - 'a' + 10;
        }
        ch++;
        byte = byte << 4;
        if (*ch) {
            if ('0' <= *ch && *ch <= '9') {
                byte += *ch - '0';
            } else if ('a' <= *ch && *ch <= 'f') {
                byte += *ch - 'a' + 10;
            }
            ch++;
        }
        [data appendBytes:&byte length:1];
    }
    return data;
}

char * hex2hexstr(unsigned char * pSrc, int nSrcLen)
{
	char * pDes = NULL;
	char * p;
	int size = 0;
	if (pSrc && nSrcLen > 0)
	{
//		pDes = new char[(nSrcLen << 1) + 1];
		size = (nSrcLen << 1) + 1;
		pDes = (char *)malloc(size * sizeof(char *));
		p = pDes;
		for (int i=0; i<nSrcLen; i++)
		{
			sprintf(p,"%02X",(unsigned char)pSrc[i]);
			p += 2;
		}
		*p = 0;
	}
	return pDes;
}

+ (void)TestXXTEA {
	const char * const g_szKey = "abcdefg";
	const char * const l_data = "qwert";
	
	unsigned char * pEncrypt = 0;
	unsigned char * pDecrypt = 0;
	int nEncryptLen = 0;
	int nDecryptLen = 0;


	nEncryptLen = xxtea_encrypt(&pEncrypt, (unsigned char*)l_data, strlen(l_data), (unsigned char*)g_szKey, strlen(g_szKey));
	
	if (nEncryptLen && pEncrypt)
	{
		char * abc = hex2hexstr(pEncrypt, nEncryptLen);
		printf("abc:%s", abc);
		
		if (abc)
			free(abc);
		
		nDecryptLen = xxtea_decrypt(&pDecrypt, pEncrypt, nEncryptLen, (unsigned char*)g_szKey, strlen(g_szKey));
		
		if (pDecrypt)
		{
			free(pDecrypt);
		}
	}
	
	if (pEncrypt)
		free(pEncrypt);
	
}

+ (NSString *)XXTEADEncryptData:(NSData *)srcData key:(NSString *)key {
	
	char *keyString = (char *)[key UTF8String];
	char *plaintext = (char *)[srcData bytes];
	
	
	unsigned char * pDecrypt = 0;
	int nDecryptLen = 0;
	
	int nEncryptLen = srcData.length;
	
	nDecryptLen = xxtea_decrypt(&pDecrypt, (unsigned char*)plaintext, nEncryptLen, 
								(unsigned char*)keyString, strlen(keyString));
	
	
	if (nDecryptLen && pDecrypt) {
		NSString *str_utf = [[NSString alloc] initWithBytes:pDecrypt 
													 length:nDecryptLen 
												   encoding:NSUTF8StringEncoding];
		free(pDecrypt);
		return str_utf;
	}
	
	
	return nil;
}

+ (NSString *)XXTEADEncryptStr:(NSString *)srcString key:(NSString *)key {
    if ([srcString length] == 0|| [key length] == 0) {
        return nil;
    }
    
	NSData *dataString = [EncryptorXXTEA dataForHexString:srcString];
    return [EncryptorXXTEA XXTEADEncryptData:dataString key:key];
}

+ (NSString *)XXTEAEncryptStr:(NSString *)srcString key:(NSString *)key {
	char *keyString = (char *)[key UTF8String];
	char *plaintext = (char *)[srcString UTF8String];
	unsigned char * pEncrypt = 0;
	int nEncryptLen = 0;
	nEncryptLen = xxtea_encrypt(&pEncrypt, (unsigned char*)plaintext, strlen(plaintext),
								(unsigned char*)keyString, strlen(keyString));
	
	if (nEncryptLen && pEncrypt) {
		char * outString = hex2hexstr(pEncrypt, nEncryptLen);
		free(pEncrypt);
		if (outString) {
			NSString *str_utf = [[NSString alloc] initWithBytes:outString 
														 length:strlen(outString) encoding:NSUTF8StringEncoding];
			free(outString);
			return str_utf;
		}
		return nil;
	}
	return nil;
}
@end
